"""
配置管理模块

该模块提供了统一的配置管理功能，支持多种配置文件格式（JSON、YAML、INI、.env）的读取和写入，
并提供配置热加载、加密解密、配置项校验等功能。

创建时间：2023-07-01
作者：Python Tools Team
"""

import os
import json
import yaml
import configparser
import time
import threading
import base64
import hashlib
from typing import Any, Dict, List, Optional, Union, Callable, TypeVar, Generic, Type, cast, Tuple
from pathlib import Path
from dotenv import load_dotenv
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from modules.logger import LogManager

# 类型定义
T = TypeVar('T')
ConfigValue = Union[str, int, float, bool, Dict[str, Any], List[Any], None]
ConfigDict = Dict[str, ConfigValue]

# 配置文件格式枚举
class ConfigFormat:
    JSON = 'json'
    YAML = 'yaml'
    INI = 'ini'
    ENV = 'env'
    
    @staticmethod
    def from_file_extension(file_path: str) -> str:
        """
        根据文件扩展名确定配置文件格式
        
        Args:
            file_path: 配置文件路径
            
        Returns:
            配置文件格式
        """
        ext = os.path.splitext(file_path)[1].lower()
        if ext in ('.json'):
            return ConfigFormat.JSON
        elif ext in ('.yaml', '.yml'):
            return ConfigFormat.YAML
        elif ext in ('.ini', '.cfg', '.conf'):
            return ConfigFormat.INI
        elif ext in ('.env'):
            return ConfigFormat.ENV
        else:
            raise ValueError(f"不支持的配置文件格式: {ext}")


# 配置验证器类
class ConfigValidator(Generic[T]):
    """配置验证器，用于验证配置项是否符合要求"""
    
    def __init__(self, 
                 field_type: Type[T], 
                 required: bool = False, 
                 default: Optional[T] = None,
                 validator: Optional[Callable[[T], bool]] = None,
                 description: str = ""):
        """初始化配置验证器
        
        Args:
            field_type: 字段类型
            required: 是否必需
            default: 默认值
            validator: 自定义验证函数
            description: 字段描述
        """
        self.field_type = field_type
        self.required = required
        self.default = default
        self.validator = validator
        self.description = description
        
    def validate(self, value: Any) -> Tuple[bool, Optional[str], Optional[T]]:
        """验证配置值
        
        Args:
            value: 要验证的值
            
        Returns:
            (是否有效, 错误信息, 转换后的值)
        """
        # 检查必需字段
        if value is None:
            if self.required:
                return False, "必需字段缺失", None
            return True, None, self.default
        
        # 类型检查和转换
        try:
            if self.field_type == bool and isinstance(value, str):
                # 特殊处理布尔值的字符串表示
                if value.lower() in ('true', 'yes', '1', 'on'):
                    typed_value = True
                elif value.lower() in ('false', 'no', '0', 'off'):
                    typed_value = False
                else:
                    return False, f"无法将 '{value}' 转换为布尔值", None
            else:
                typed_value = self.field_type(value)
        except (ValueError, TypeError):
            return False, f"类型不匹配，期望 {self.field_type.__name__}，实际为 {type(value).__name__}", None
        
        # 自定义验证
        if self.validator and not self.validator(typed_value):
            return False, "自定义验证失败", None
            
        return True, None, typed_value


# 配置加密器类
class ConfigEncryptor:
    """配置加密器，用于配置项的加密和解密"""
    
    def __init__(self, secret_key: str, salt: bytes = None):
        """初始化配置加密器
        
        Args:
            secret_key: 加密密钥
            salt: 盐值，用于密钥派生
        """
        if salt is None:
            # 使用固定的盐值，在生产环境中应该使用随机生成的盐值并安全存储
            salt = b'python_tools_config_salt'
            
        # 从密钥派生Fernet密钥
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(secret_key.encode()))
        self.cipher = Fernet(key)
        
    def encrypt(self, value: str) -> str:
        """加密字符串值
        
        Args:
            value: 要加密的字符串
            
        Returns:
            加密后的字符串
        """
        return self.cipher.encrypt(value.encode()).decode()
    
    def decrypt(self, encrypted_value: str) -> str:
        """解密字符串值
        
        Args:
            encrypted_value: 加密的字符串
            
        Returns:
            解密后的字符串
        """
        try:
            return self.cipher.decrypt(encrypted_value.encode()).decode()
        except Exception as e:
            raise ValueError(f"解密失败: {str(e)}")


# 配置管理器类
class ConfigManager:
    """配置管理器，提供统一的配置管理接口"""
    
    # 单例模式实现
    _instances: Dict[str, 'ConfigManager'] = {}
    
    @classmethod
    def get_instance(cls, name: str = 'default') -> 'ConfigManager':
        """获取ConfigManager实例（单例模式）
        
        Args:
            name: 实例名称，用于支持多个配置管理器实例
            
        Returns:
            ConfigManager实例
        """
        if name not in cls._instances:
            cls._instances[name] = ConfigManager(name)
        return cls._instances[name]
    
    def __init__(self, name: str = 'default'):
        """初始化配置管理器
        
        Args:
            name: 配置管理器名称
        """
        self.name = name
        self.config_data: ConfigDict = {}
        self.config_files: Dict[str, Dict[str, Any]] = {}
        self.validators: Dict[str, ConfigValidator] = {}
        self.encryptor: Optional[ConfigEncryptor] = None
        self.auto_reload_interval: Optional[int] = None
        self._stop_auto_reload = threading.Event()
        self._auto_reload_thread: Optional[threading.Thread] = None
        self.logger = LogManager.get_both_logger(name=f"config_manager_{name}")
        
    def load_config(self, 
                   file_path: str, 
                   format_type: Optional[str] = None, 
                   namespace: str = '',
                   auto_reload: bool = False,
                   reload_interval: int = 60) -> bool:
        """加载配置文件
        
        Args:
            file_path: 配置文件路径
            format_type: 配置文件格式，如果为None则自动从文件扩展名判断
            namespace: 配置命名空间，用于隔离不同配置文件的配置项
            auto_reload: 是否自动重新加载
            reload_interval: 自动重新加载间隔（秒）
            
        Returns:
            是否成功加载
        """
        try:
            file_path = os.path.abspath(file_path)
            
            if not os.path.exists(file_path):
                self.logger.error(f"配置文件不存在: {file_path}")
                return False
                
            # 确定配置文件格式
            if format_type is None:
                format_type = ConfigFormat.from_file_extension(file_path)
                
            # 加载配置文件
            config_data = self._load_file(file_path, format_type)
            
            # 保存文件信息，用于自动重新加载
            self.config_files[file_path] = {
                'format': format_type,
                'namespace': namespace,
                'last_modified': os.path.getmtime(file_path)
            }
            
            # 将配置数据合并到主配置中
            self._merge_config(config_data, namespace)
            
            # 设置自动重新加载
            if auto_reload:
                self.enable_auto_reload(reload_interval)
                
            self.logger.info(f"成功加载配置文件: {file_path}")
            return True
            
        except Exception as e:
            self.logger.error(f"加载配置文件失败: {file_path}, 错误: {str(e)}", exc_info=True)
            return False
    
    def _load_file(self, file_path: str, format_type: str) -> ConfigDict:
        """根据格式加载配置文件
        
        Args:
            file_path: 配置文件路径
            format_type: 配置文件格式
            
        Returns:
            配置数据字典
        """
        if format_type == ConfigFormat.JSON:
            with open(file_path, 'r', encoding='utf-8') as f:
                return json.load(f)
                
        elif format_type == ConfigFormat.YAML:
            with open(file_path, 'r', encoding='utf-8') as f:
                return yaml.safe_load(f)
                
        elif format_type == ConfigFormat.INI:
            config = configparser.ConfigParser()
            config.read(file_path, encoding='utf-8')
            # 将ConfigParser对象转换为字典
            result: ConfigDict = {}
            for section in config.sections():
                result[section] = {}
                for key, value in config[section].items():
                    result[section][key] = value
            return result
            
        elif format_type == ConfigFormat.ENV:
            # 加载.env文件
            env_vars = {}
            load_dotenv(file_path)
            # 读取文件中的所有环境变量
            with open(file_path, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        if '=' in line:
                            key, value = line.split('=', 1)
                            env_vars[key.strip()] = value.strip().strip('"\'')
            return env_vars
            
        else:
            raise ValueError(f"不支持的配置文件格式: {format_type}")
    
    def _merge_config(self, config_data: ConfigDict, namespace: str = '') -> None:
        """将配置数据合并到主配置中
        
        Args:
            config_data: 配置数据
            namespace: 配置命名空间
        """
        if namespace:
            # 如果指定了命名空间，将配置放在命名空间下
            if namespace not in self.config_data:
                self.config_data[namespace] = {}
            self._deep_update(self.config_data[namespace], config_data)
        else:
            # 否则直接合并到根级别
            self._deep_update(self.config_data, config_data)
    
    def _deep_update(self, target: Dict[str, Any], source: Dict[str, Any]) -> None:
        """深度更新字典
        
        Args:
            target: 目标字典
            source: 源字典
        """
        for key, value in source.items():
            if key in target and isinstance(target[key], dict) and isinstance(value, dict):
                self._deep_update(target[key], value)
            else:
                target[key] = value
    
    def save_config(self, file_path: str, format_type: Optional[str] = None, namespace: str = '') -> bool:
        """保存配置到文件
        
        Args:
            file_path: 配置文件路径
            format_type: 配置文件格式，如果为None则自动从文件扩展名判断
            namespace: 配置命名空间，如果指定则只保存该命名空间下的配置
            
        Returns:
            是否成功保存
        """
        try:
            file_path = os.path.abspath(file_path)
            
            # 确定配置文件格式
            if format_type is None:
                format_type = ConfigFormat.from_file_extension(file_path)
            
            # 确保目录存在
            os.makedirs(os.path.dirname(file_path), exist_ok=True)
            
            # 获取要保存的配置数据
            if namespace and namespace in self.config_data:
                config_to_save = self.config_data[namespace]
            else:
                config_to_save = self.config_data
            
            # 根据格式保存配置
            if format_type == ConfigFormat.JSON:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(config_to_save, f, indent=2, ensure_ascii=False)
                    
            elif format_type == ConfigFormat.YAML:
                with open(file_path, 'w', encoding='utf-8') as f:
                    yaml.dump(config_to_save, f, default_flow_style=False, allow_unicode=True)
                    
            elif format_type == ConfigFormat.INI:
                config = configparser.ConfigParser()
                # 将字典转换为ConfigParser对象
                for section, section_data in config_to_save.items():
                    if isinstance(section_data, dict):
                        config[section] = {}
                        for key, value in section_data.items():
                            if isinstance(value, (str, int, float, bool)):
                                config[section][key] = str(value)
                with open(file_path, 'w', encoding='utf-8') as f:
                    config.write(f)
                    
            elif format_type == ConfigFormat.ENV:
                with open(file_path, 'w', encoding='utf-8') as f:
                    for key, value in config_to_save.items():
                        if isinstance(value, (str, int, float, bool)):
                            f.write(f"{key}={value}\n")
                            
            else:
                raise ValueError(f"不支持的配置文件格式: {format_type}")
                
            self.logger.info(f"成功保存配置到文件: {file_path}")
            return True
            
        except Exception as e:
            self.logger.error(f"保存配置文件失败: {file_path}, 错误: {str(e)}", exc_info=True)
            return False
    
    def reload_config(self) -> bool:
        """重新加载所有配置文件
        
        Returns:
            是否成功重新加载
        """
        success = True
        # 清空当前配置
        self.config_data = {}
        
        # 重新加载所有配置文件
        for file_path, file_info in self.config_files.items():
            if not self.load_config(
                file_path, 
                format_type=file_info['format'], 
                namespace=file_info['namespace']
            ):
                success = False
                
        return success
    
    def check_and_reload_if_changed(self) -> bool:
        """检查配置文件是否已更改，如果是则重新加载
        
        Returns:
            是否有文件被重新加载
        """
        reloaded = False
        for file_path, file_info in self.config_files.items():
            if os.path.exists(file_path):
                current_mtime = os.path.getmtime(file_path)
                if current_mtime > file_info['last_modified']:
                    self.logger.info(f"检测到配置文件已更改，重新加载: {file_path}")
                    # 只重新加载已更改的文件
                    if self.load_config(
                        file_path, 
                        format_type=file_info['format'], 
                        namespace=file_info['namespace']
                    ):
                        reloaded = True
                    file_info['last_modified'] = current_mtime
        return reloaded
    
    def enable_auto_reload(self, interval: int = 60) -> None:
        """启用配置自动重新加载
        
        Args:
            interval: 检查间隔（秒）
        """
        self.auto_reload_interval = interval
        
        # 停止现有的自动重新加载线程
        self.disable_auto_reload()
        
        # 启动新的自动重新加载线程
        self._stop_auto_reload.clear()
        self._auto_reload_thread = threading.Thread(
            target=self._auto_reload_worker,
            daemon=True
        )
        self._auto_reload_thread.start()
        self.logger.info(f"已启用配置自动重新加载，间隔: {interval}秒")
    
    def disable_auto_reload(self) -> None:
        """禁用配置自动重新加载"""
        if self._auto_reload_thread and self._auto_reload_thread.is_alive():
            self._stop_auto_reload.set()
            self._auto_reload_thread.join(timeout=1)
            self._auto_reload_thread = None
            self.logger.info("已禁用配置自动重新加载")
    
    def _auto_reload_worker(self) -> None:
        """自动重新加载工作线程"""
        while not self._stop_auto_reload.is_set():
            try:
                # 检查配置文件是否已更改
                self.check_and_reload_if_changed()
            except Exception as e:
                self.logger.error(f"自动重新加载过程中出错: {str(e)}", exc_info=True)
            
            # 等待下一次检查
            self._stop_auto_reload.wait(self.auto_reload_interval)
    
    def set_encryptor(self, secret_key: str, salt: bytes = None) -> None:
        """设置配置加密器
        
        Args:
            secret_key: 加密密钥
            salt: 盐值，用于密钥派生
        """
        self.encryptor = ConfigEncryptor(secret_key, salt)
        self.logger.info("已设置配置加密器")
    
    def encrypt_value(self, value: str) -> str:
        """加密配置值
        
        Args:
            value: 要加密的值
            
        Returns:
            加密后的值
        """
        if not self.encryptor:
            raise ValueError("未设置配置加密器，请先调用 set_encryptor 方法")
        return self.encryptor.encrypt(value)
    
    def decrypt_value(self, encrypted_value: str) -> str:
        """解密配置值
        
        Args:
            encrypted_value: 加密的值
            
        Returns:
            解密后的值
        """
        if not self.encryptor:
            raise ValueError("未设置配置加密器，请先调用 set_encryptor 方法")
        return self.encryptor.decrypt(encrypted_value)
    
    def register_validator(self, key: str, validator: ConfigValidator) -> None:
        """注册配置验证器
        
        Args:
            key: 配置键
            validator: 配置验证器
        """
        self.validators[key] = validator
        self.logger.debug(f"已注册配置验证器: {key}")
    
    def validate_config(self) -> Tuple[bool, Dict[str, str]]:
        """验证所有配置项
        
        Returns:
            (是否全部有效, 错误信息字典)
        """
        all_valid = True
        errors = {}
        
        # 验证所有已注册的配置项
        for key, validator in self.validators.items():
            value = self.get(key)
            valid, error_msg, _ = validator.validate(value)
            if not valid:
                all_valid = False
                errors[key] = error_msg or "验证失败"
                
        return all_valid, errors
    
    def get(self, key: str, default: Any = None) -> Any:
        """获取配置项值
        
        Args:
            key: 配置键，支持点号分隔的多级键
            default: 默认值，如果配置项不存在则返回该值
            
        Returns:
            配置项值
        """
        try:
            # 处理点号分隔的多级键
            parts = key.split('.')
            value = self.config_data
            
            for part in parts:
                if isinstance(value, dict) and part in value:
                    value = value[part]
                else:
                    return default
                    
            return value
        except Exception:
            return default
    
    def get_int(self, key: str, default: int = 0) -> int:
        """获取整数类型的配置项值
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            整数值
        """
        value = self.get(key, default)
        try:
            return int(value)
        except (TypeError, ValueError):
            return default
    
    def get_float(self, key: str, default: float = 0.0) -> float:
        """获取浮点数类型的配置项值
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            浮点数值
        """
        value = self.get(key, default)
        try:
            return float(value)
        except (TypeError, ValueError):
            return default
    
    def get_bool(self, key: str, default: bool = False) -> bool:
        """获取布尔类型的配置项值
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            布尔值
        """
        value = self.get(key, default)
        if isinstance(value, bool):
            return value
        elif isinstance(value, str):
            return value.lower() in ('true', 'yes', '1', 'on')
        elif isinstance(value, (int, float)):
            return value != 0
        return default
    
    def get_string(self, key: str, default: str = '') -> str:
        """获取字符串类型的配置项值
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            字符串值
        """
        value = self.get(key, default)
        if value is None:
            return default
        return str(value)
    
    def get_dict(self, key: str, default: Dict[str, Any] = None) -> Dict[str, Any]:
        """获取字典类型的配置项值
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            字典值
        """
        if default is None:
            default = {}
        value = self.get(key, default)
        if isinstance(value, dict):
            return value
        return default
    
    def get_list(self, key: str, default: List[Any] = None) -> List[Any]:
        """获取列表类型的配置项值
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            列表值
        """
        if default is None:
            default = []
        value = self.get(key, default)
        if isinstance(value, list):
            return value
        return default
    
    def get_encrypted(self, key: str, default: str = '') -> str:
        """获取加密的配置项值并解密
        
        Args:
            key: 配置键
            default: 默认值
            
        Returns:
            解密后的值
        """
        value = self.get_string(key, default)
        if value == default:
            return default
            
        try:
            if self.encryptor:
                return self.encryptor.decrypt(value)
            else:
                self.logger.warning("未设置配置加密器，无法解密配置值")
                return value
        except Exception as e:
            self.logger.error(f"解密配置值失败: {key}, 错误: {str(e)}")
            return default
    
    def set(self, key: str, value: Any) -> None:
        """设置配置项值
        
        Args:
            key: 配置键，支持点号分隔的多级键
            value: 配置值
        """
        # 处理点号分隔的多级键
        parts = key.split('.')
        target = self.config_data
        
        # 遍历除最后一个部分外的所有部分
        for part in parts[:-1]:
            if part not in target or not isinstance(target[part], dict):
                target[part] = {}
            target = target[part]
            
        # 设置最后一个部分的值
        target[parts[-1]] = value
    
    def set_encrypted(self, key: str, value: str) -> None:
        """设置加密的配置项值
        
        Args:
            key: 配置键
            value: 要加密的值
        """
        if not self.encryptor:
            raise ValueError("未设置配置加密器，请先调用 set_encryptor 方法")
            
        encrypted_value = self.encryptor.encrypt(value)
        self.set(key, encrypted_value)
    
    def remove(self, key: str) -> bool:
        """删除配置项
        
        Args:
            key: 配置键，支持点号分隔的多级键
            
        Returns:
            是否成功删除
        """
        # 处理点号分隔的多级键
        parts = key.split('.')
        target = self.config_data
        
        # 遍历除最后一个部分外的所有部分
        for part in parts[:-1]:
            if part not in target or not isinstance(target[part], dict):
                return False
            target = target[part]
            
        # 删除最后一个部分
        if parts[-1] in target:
            del target[parts[-1]]
            return True
        return False
    
    def clear(self, namespace: str = '') -> None:
        """清空配置
        
        Args:
            namespace: 配置命名空间，如果指定则只清空该命名空间下的配置
        """
        if namespace:
            if namespace in self.config_data:
                self.config_data[namespace] = {}
        else:
            self.config_data = {}
    
    def get_all(self, namespace: str = '') -> Dict[str, Any]:
        """获取所有配置项
        
        Args:
            namespace: 配置命名空间，如果指定则只返回该命名空间下的配置
            
        Returns:
            配置项字典
        """
        if namespace:
            return self.config_data.get(namespace, {}).copy()
        return self.config_data.copy()


# 便捷函数，获取默认配置管理器实例
def get_config_manager(name: str = 'default') -> ConfigManager:
    """获取配置管理器实例
    
    Args:
        name: 配置管理器名称
        
    Returns:
        ConfigManager实例
    """
    return ConfigManager.get_instance(name)


if __name__ == '__main__':
    # 示例用法
    pass